#include "stdafx.h"
#include "IRunnable.h"
#include "FpsController.h"
#include "Utility.h"
#include "ProcessManager.h"
#include "MemTrack.h"

extern bs::FpsController* gFpsController;

namespace bs
{
	const int MAX_ID = 1000000;

	ProcessManager::ProcessManager() : nextId(0)
	{
	}

	ProcessManager::~ProcessManager()
	{
		while(!this->timeProcesses.empty())
		{
			ProcessInfo* info = this->timeProcesses.front();
			this->timeProcesses.pop_front();
			this->DeleteProcess(info);
		}

		while(!this->frameProcesses.empty())
		{
			ProcessInfo* info = this->frameProcesses.front();
			this->frameProcesses.pop_front();
			this->DeleteProcess(info);
		}
	}

	ProcessManager& ProcessManager::Instance()
	{
		static ProcessManager manager;

		return manager;
	}

	bool ProcessManager::NewProcess(int* id, bs::IRunnable* runner, ProcessType type, int priority, size_t begin, 
		size_t duration, size_t period, void* userData, bool owned/* = false */, bool releaseData/* = false*/)
	{
		if(!runner || !gFpsController)
		{
			LOG(LL_ERROR, "NewProcess() failed by invalid runner or fpscontroller!");

			return false;
		}

		if(type==Time && duration && begin+duration<=gFpsController->GetCurTime()
			|| type==Frame && duration && begin+duration<=gFpsController->GetCurFrame())
		{
			LOG(LL_ERROR, "NewProcess() failed by invalid time setting!");

			return false;
		}

		ProcessInfo* info = new ProcessInfo;
		info->id = (++ this->nextId) % MAX_ID;
		info->runner = runner;
		info->type = type;
		info->priority = priority;
		info->status = Active;
		info->begin = begin;
		info->end = duration ? begin+duration-1 : 0;
		info->period = period;
		info->userData = userData;
		info->owned = owned;
		info->releaseData = releaseData;

		info->next = begin;
		info->last = begin;
		info->suspendEnd = 0;

		this->InsertProcess(info);

		if(id)
		{
			*id = info->id;
		}

		LOG(LL_INFO, "Process(id: %d type: %d priority: %d) create sucessfully", info->id, info->type, info->priority);

		return true;
	}

	void ProcessManager::Execute( size_t time, size_t frames, size_t elapse )
{
		this->ExecuteProcessList(this->timeProcesses, time, frames, elapse);

		this->ExecuteProcessList(this->frameProcesses, time, frames, elapse);
	}

	void ProcessManager::TerminateProcess(int id)
	{
		ProcessInfo* info = this->GetProcess(id);
		if(info)
		{
			info->status = Terminated;
		}
	}

	void ProcessManager::TerminateProcess(IRunnable* runner)
	{
		for(ProcessInfoList::iterator itr=this->timeProcesses.begin();
			itr!=this->timeProcesses.end(); ++itr)
		{
			if((*itr)->runner == runner)
			{
				(*itr)->status = Terminated;
			}
		}

		for(ProcessInfoList::iterator itr=this->frameProcesses.begin();
			itr!=this->frameProcesses.end(); ++itr)
		{
			if((*itr)->runner == runner)
			{
				(*itr)->status = Terminated;
			}
		}
	}

	void ProcessManager::TerminateAllProcess()
	{
		for(ProcessInfoList::iterator itr=this->timeProcesses.begin();
			itr!=this->timeProcesses.end(); ++itr)
		{
			(*itr)->status = Terminated;
		}

		for(ProcessInfoList::iterator itr=this->frameProcesses.begin();
			itr!=this->frameProcesses.end(); ++itr)
		{
			(*itr)->status = Terminated;
		}
	}

	void ProcessManager::SuspendProcess(int id, size_t duration/* = 0 */)
	{
		ProcessInfo* info = this->GetProcess(id);
		if(info && info->status==Active)
		{
			info->status = Suspended;
			info->suspendEnd = duration ? info->last+duration : 0;
		}
	}

	void ProcessManager::SuspendAllProcess(size_t duration/* = 0 */)
	{
		for(ProcessInfoList::iterator itr=this->timeProcesses.begin();
			itr!=this->timeProcesses.end(); ++itr)
		{
			if((*itr)->status==Active)
			{
				(*itr)->status = Suspended;
				(*itr)->suspendEnd = duration ? (*itr)->last+duration : 0;
			}
		}

		for(ProcessInfoList::iterator itr=this->frameProcesses.begin();
			itr!=this->frameProcesses.end(); ++itr)
		{
			if((*itr)->status==Active)
			{
				(*itr)->status = Suspended;
				(*itr)->suspendEnd = duration ? (*itr)->last+duration : 0;
			}
		}
	}

	void ProcessManager::ResumeProcess(int id)
	{
		ProcessInfo* info = this->GetProcess(id);
		if(info && info->status==Suspended)
		{
			info->status = Active;
		}
	}

	void ProcessManager::ResumeAllProcess()
	{
		for(ProcessInfoList::iterator itr=this->timeProcesses.begin();
			itr!=this->timeProcesses.end(); ++itr)
		{
			if((*itr)->status==Suspended)
			{
				(*itr)->status = Active;
			}
		}

		for(ProcessInfoList::iterator itr=this->frameProcesses.begin();
			itr!=this->frameProcesses.end(); ++itr)
		{
			if((*itr)->status==Suspended)
			{
				(*itr)->status = Active;
			}
		}
	}

	void ProcessManager::InsertProcess(ProcessInfo* info)
	{
		ProcessInfoList* list = NULL;
		list = info->type==Time ? &this->timeProcesses : &this->frameProcesses;

		for(ProcessInfoList::iterator itr=list->begin(); itr!=list->end(); ++itr)
		{
			if(info->next<(*itr)->next || info->next==(*itr)->next && info->priority>(*itr)->priority)
			{
				list->insert(itr, info);
				return;
			}
		}

		list->push_back(info);
	}

	void ProcessManager::ExecuteProcessList( ProcessInfoList& list, size_t time, size_t frames, size_t elapse )
    {
		while(!list.empty())
		{
			ProcessInfo* info = list.front();
		
			size_t now = info->type==Time ? time : frames;

			if(info->status == Terminated)
			{
				list.pop_front();
				this->DeleteProcess(info);
				continue;
			}

			if(info->next > now)
				break;

			if(info->status == Active)
			{
				info->runner->Run(info->id, time, frames, elapse, info->userData);
			}
			else
			{
				if(!info->suspendEnd && info->suspendEnd<=now)
				{
					info->status = Active;
				}
			}

			list.pop_front();
			info->last = now;
			info->next = now + info->period;
			if(info->next<=info->end || !info->end)
			{
				this->InsertProcess(info);
			}
			else
			{
				this->DeleteProcess(info);
			}
		}
	}

	void ProcessManager::DeleteProcess(ProcessInfo* info)
	{
		if(info->releaseData)
			info->runner->ReleaseUserData();

		if(info->owned)
			SAFE_DELETE(info->runner);

		LOG(LL_INFO, "Process(id: %d type: %d priority: %d) destory sucessfully", info->id, info->type, info->priority);

		SAFE_DELETE(info);
	}

	ProcessManager::ProcessInfo* ProcessManager::GetProcess(int id)
	{
		for(ProcessInfoList::iterator itr=this->timeProcesses.begin();
			itr!=this->timeProcesses.end(); ++itr)
		{
			if((*itr)->id == id)
			{
				return *itr;
			}
		}

		for(ProcessInfoList::iterator itr=this->frameProcesses.begin();
			itr!=this->frameProcesses.end(); ++itr)
		{
			if((*itr)->id == id)
			{
				return *itr;
			}
		}

		return NULL;
	}

}
